﻿using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using NpcFramework.Traffic;
using UnityEditor;
#endif
using UnityEngine;

namespace NpcFramework
{
    //[ExecuteAlways]
    public class NpcWayPath : MonoBehaviour
    {
        public int WayID;
        public bool IsLoop;
        public bool HaveWidth = true;
        public List<NpcWayPoint> WayPoints;
        public WayPathType WayPathType;

        public float newPointOffset = 5;
        
        public NpcWayPathData GetData()
        {
            if (WayPoints == null || WayPoints.Count == 0)
            {
                return null;
            }
            NpcWayPathData pathData = new NpcWayPathData();
            List<NpcWayPointData> wayPointData = new List<NpcWayPointData>();

            for (int i = 0; i < WayPoints.Count; i++)
            {
                var p = WayPoints[i];
                
                NpcWayPointData pointData = new NpcWayPointData();
                pointData.Index = p.Index;
                if (p.PrevWayPoint)
                {
                    pointData.PrevIndex = p.PrevWayPoint.Index;
                }
                else
                {
                    pointData.PrevIndex = -1;
                }
                
                if (p.NextWayPoint)
                {
                    pointData.NextIndex = p.NextWayPoint.Index;
                }
                else
                {
                    pointData.NextIndex = -1;
                }
                pointData.BranchIndex = new List<BranchPointData>();

                for (int j = 0; j < p.branchPoints.Count; j++)
                {
                    NpcWayPoint bp = p.branchPoints[j];
                    pointData.BranchIndex.Add(new BranchPointData()
                    {
                        Index = bp.Index,
                        BranchType =  bp.branchType,
                        Ratio = bp.branchRatio,
                    });
                }

                pointData.Position = p.transform.position;
                pointData.Right = p.transform.right;
                pointData.Width = p.width;
                pointData.IsGenerateCar = p.IsGenerateCar;
                pointData.IsGenerateNpc = p.IsGenerateNpc ;
                pointData.BTreeName = p.BTreePath;
                pointData.WayId = WayID;
                if ( p.TrafficLight)
                {
                    pointData.TrafficLightID = p.TrafficLight.trafficLightID;
                }
                else
                {
                    pointData.TrafficLightID = -1;
                }
                if (p.NextWayPoint)
                {
                    pointData.Forward = (p.NextWayPoint.transform.position - p.transform.position).normalized;
                }
                wayPointData.Add(pointData);
            }

            // 标记分支点
            foreach (var pointData in wayPointData)
            {
                pointData.IsBranchPoint = true;
            }
            
            var wp = WayPoints[0];

            while (wp.NextWayPoint && wp.NextWayPoint!=WayPoints[0])
            {
                wayPointData[wp.Index].IsBranchPoint = false;
                wp = wp.NextWayPoint;
            }

            if (wp)
            {
                wayPointData[wp.Index].IsBranchPoint = false;
            }
            
            pathData.WayID = WayID;
            pathData.IsLoop = IsLoop;
            pathData.WayPoints = wayPointData;
            pathData.WayPathType = WayPathType;
            return pathData;
        }

#if UNITY_EDITOR

        private void OnEnable()
        {
            lastIsLoop = IsLoop;
        }

        public bool IsBranchPoint(NpcWayPoint point)
        {
            if (point.PrevWayPoint)
            {
                if (point.PrevWayPoint.branchPoints != null)
                {
                    foreach (var bp in point.PrevWayPoint.branchPoints)
                    {
                        if (bp == point)
                        {
                            // 为分支点
                            return true;
                        }
                    }
                }
            }

            return false;
        }
        
        public void AddWayPoint()
        {
            NpcWayPath npcWayPath = this;

            var wayPoints = npcWayPath.WayPoints;

            if (wayPoints == null)
            {
                wayPoints =  npcWayPath.WayPoints = new List<NpcWayPoint>();
            }

            NpcWayPoint npcWayPoint = new GameObject(wayPoints.Count.ToString()).AddComponent<NpcWayPoint>();
            npcWayPoint.WayPath = npcWayPath;
            npcWayPoint.Index = wayPoints.Count;
            npcWayPoint.transform.position = Vector3.zero;
            if (wayPoints.Count > 0)
            {
                var wp = GetEndPoint();

                if (wp.PrevWayPoint!=null)
                {
                    Vector3 offset = (wp.transform.position - wp.PrevWayPoint.transform.position).normalized * newPointOffset;
                    npcWayPoint.transform.position = wp.transform.position + offset;
                }
                else
                {
                    npcWayPoint.transform.position = wp.transform.position + new Vector3(1f,0,1f);
                }
                
                npcWayPoint.transform.right = wp.transform.right;

                
                npcWayPoint.PrevWayPoint = wp;
                npcWayPoint.width = wp.width;
                wp.NextWayPoint = npcWayPoint;
                if (npcWayPath.IsLoop)
                {
                    npcWayPoint.NextWayPoint = wayPoints[0];
                    wayPoints[0].PrevWayPoint = npcWayPoint;
                }
            }
                
            wayPoints.Add(npcWayPoint);
            npcWayPoint.transform.parent = npcWayPath.transform;
            RefreshLight();

            Selection.activeGameObject = npcWayPoint.gameObject;
        }
        
        public void InsertWayPoint(int i)
        {
            NpcWayPath npcWayPath = this;
            var wp = GetEndPoint();
            
            var wayPoints = npcWayPath.WayPoints;

            if (wp.Index == i)    // 如果是尾部节点
            {
                AddWayPoint();
                return;
            }
            NpcWayPoint npcWayPoint = new GameObject().AddComponent<NpcWayPoint>();
            npcWayPoint.WayPath = npcWayPath;
            if (wayPoints[i].NextWayPoint == null)  // 分支的尾部
            {
                int idx = wayPoints.Count;
                npcWayPoint.Index = idx;
                npcWayPoint.width = wayPoints[i].width;
                if (wayPoints[i].PrevWayPoint)
                {
                    Vector3 p = 2 * wayPoints[i].transform.position - wayPoints[i].PrevWayPoint.transform.position;
                    p.y = 0;
                    npcWayPoint.transform.position = p;
                }
                else
                {
                    npcWayPoint.transform.position = wayPoints[i].transform.position + wayPoints[i].transform.forward * 5;
                }
                npcWayPoint.transform.right = wayPoints[i].transform.right;
                npcWayPoint.transform.parent = npcWayPath.transform;

                npcWayPoint.PrevWayPoint = wayPoints[i];
                wayPoints[i].NextWayPoint = npcWayPoint;
                wayPoints.Add(npcWayPoint);
                npcWayPoint.transform.name = idx.ToString();
                Selection.activeGameObject = npcWayPoint.gameObject;
                return;
            }
            

            npcWayPoint.Index = i;
            npcWayPoint.width = (wayPoints[i].width + wayPoints[i].NextWayPoint.width) / 2f;
            npcWayPoint.transform.position =
                (wayPoints[i].transform.position + wayPoints[i].NextWayPoint.transform.position) / 2f;
            npcWayPoint.transform.right = (wayPoints[i].transform.right + wayPoints[i].NextWayPoint.transform.right).normalized;
            npcWayPoint.transform.parent = npcWayPath.transform;
            
            npcWayPoint.PrevWayPoint = wayPoints[i];
            npcWayPoint.NextWayPoint = wayPoints[i].NextWayPoint;
            
            wayPoints[i].NextWayPoint.PrevWayPoint = npcWayPoint;
            wayPoints[i].NextWayPoint = npcWayPoint;

            wayPoints.Insert(i+1,npcWayPoint);
            
            for (int j = 0; j < npcWayPath.WayPoints.Count; j++)
            {
                npcWayPath.WayPoints[j].Index = j;
                npcWayPath.WayPoints[j].transform.name = j.ToString();

                npcWayPath.WayPoints[j].transform.parent = null;
                npcWayPath.WayPoints[j].transform.parent = npcWayPath.transform;
            }
            RefreshLight();

            Selection.activeGameObject = npcWayPoint.gameObject;
        }
        
        public void InsertBranchPoint(int i)
        {
            NpcWayPath npcWayPath = this;
            var wayPoints = npcWayPath.WayPoints;
            
            NpcWayPoint npcWayPoint = new GameObject().AddComponent<NpcWayPoint>();
            npcWayPoint.WayPath = npcWayPath;
            if (wayPoints[i].branchPoints == null)
            {
                wayPoints[i].branchPoints = new List<NpcWayPoint>();
            }
            
            wayPoints[i].branchPoints.Add(npcWayPoint);
            npcWayPoint.Index = wayPoints.Count;
            npcWayPoint.PrevWayPoint = wayPoints[i];
            npcWayPoint.width = wayPoints[i].width;
            Vector3 pos = (wayPoints[i].transform.position + wayPoints[i].transform.right * 5);
            pos.y = 0;
            npcWayPoint.transform.position = pos;
            npcWayPoint.transform.right = wayPoints[i].transform.forward;
            npcWayPoint.transform.parent = npcWayPath.transform;

            npcWayPoint.transform.name = wayPoints.Count.ToString();
            
            wayPoints.Add(npcWayPoint);

            RefreshLight();

            Selection.activeGameObject = npcWayPoint.gameObject;
        }
        
        public void DeleteIndexPoint(NpcWayPoint point,bool destroyCallBack = false)
        {
            if (!this.transform || !this.transform.gameObject)
            {
                return;
            }
            
            if (!WayPoints.Contains(point))
            {
                return;
            }

            if (point.TrafficLight!=null)
            {
                // 被红绿灯引用了
                point.TrafficLight.RemovePointMap(point);
            }
            
            bool isBranchPoint = IsBranchPoint(point);

            if (isBranchPoint)
            {
                if (point.PrevWayPoint)
                {
                    point.PrevWayPoint.branchPoints.Remove(point);
                }

                if (point.NextWayPoint)
                {
                    if (point.NextWayPoint.PrevWayPoint == point)
                    {
                        point.NextWayPoint.PrevWayPoint = null;
                    }
                }
            }
            else
            {
                if (point.branchPoints!=null)
                {
                    foreach (var branchPoint in point.branchPoints)
                    {
                        branchPoint.PrevWayPoint = null;
                    }
                }

                if (point.PrevWayPoint)
                {
                    point.PrevWayPoint.NextWayPoint = point.NextWayPoint;
                }

                if (point.NextWayPoint)
                {
                    point.NextWayPoint.PrevWayPoint = point.PrevWayPoint;
                }
            }
            
            WayPoints.RemoveAt(point.Index);
            if (!destroyCallBack)
            {
                GameObject.DestroyImmediate(point.gameObject);
            }
            
            for (int j = 0; j < WayPoints.Count; j++)
            {
                WayPoints[j].Index = j;
                WayPoints[j].transform.name = j.ToString();

                WayPoints[j].transform.parent = null;
                WayPoints[j].transform.parent = transform;
            }

            RefreshLight();

        } 
        
        private NpcWayPoint GetEndPoint()
        {
            var wp = WayPoints[0];
			if(wp == null){
				Debug.LogError("export path failed :" + WayID);
			}
			
            while (wp.NextWayPoint && wp.NextWayPoint!=WayPoints[0])
            {
                wp = wp.NextWayPoint; 
				if(wp == null){
					Debug.LogError("export path failed :" + WayID);
					break; 
				}
            }

            return wp;
        }
        
        
        private bool lastIsLoop;
        public void Refresh()
        {
            transform.name = $"Path_{WayID}";

            RefreshLight();
            
             if (lastIsLoop == IsLoop)
             {
                 return;
             }

             if (WayPoints.Count <= 2)
             {
                 return;
             }
            
             NpcWayPoint wayPoint = GetEndPoint();
            
             if (IsLoop)
             {
                 if (wayPoint.NextWayPoint!=WayPoints[0])
                 {
                     wayPoint.NextWayPoint = WayPoints[0];
                 }
             }
             else
             {
                 if (wayPoint.NextWayPoint!=null)
                 {
                     wayPoint.NextWayPoint = null;
                 }
             }

            lastIsLoop = IsLoop;



        }

        private void RefreshLight()
        {
            // 强制刷下红绿灯
            TrafficLight[] trafficLights = FindObjectsOfType<TrafficLight>();
            if (trafficLights!=null)
            {
                foreach (var trafficLight in trafficLights)
                {
                    trafficLight.OnSaved();
                }
            }
        }


        private void OnDrawGizmos()
        {
            if (WayPoints == null || WayPoints.Count <= 1)
            {
                return;
            }
            
            for (int i = 0; i < WayPoints.Count; i++)
            {
                if (WayPoints[i] != null)
                {
                    if (WayPoints[i].NextWayPoint)
                    {
                        NpcWayPoint current = WayPoints[i];
                        NpcWayPoint NextWayPoint = WayPoints[i].NextWayPoint;

                        if (WayPathType == WayPathType.WalkNpc)
                        {
                            Gizmos.color = Color.green;
                        }
                        else
                        {
                            Gizmos.color = Color.yellow;
                        }
                        Gizmos.DrawLine(WayPoints[i].transform.position + Vector3.up * 0.5f,
                            WayPoints[i].NextWayPoint.transform.position + Vector3.up * 0.5f);

                        if (WayPathType != WayPathType.Car)
                        {
                            Gizmos.color = Color.white;
                            Gizmos.DrawLine(
                                current.transform.position + (current.transform.right * current.width / 2f) +
                                Vector3.up * 0.5f,
                                current.transform.position - (current.transform.right * current.width / 2f) +
                                Vector3.up * 0.5f);

                            if (NextWayPoint != null)
                            {
                                Gizmos.color = Color.cyan;
                                Vector3 offset = current.transform.right * -current.width / 2f;
                                Vector3 offsetTo = NextWayPoint.transform.right * -NextWayPoint.width / 2f;

                                Gizmos.DrawLine(current.transform.position + offset + Vector3.up * 0.5f,
                                    NextWayPoint.transform.position + offsetTo + Vector3.up * 0.5f);

                                Gizmos.color = Color.magenta;
                                offset = current.transform.right * current.width / 2f;
                                offsetTo = NextWayPoint.transform.right * NextWayPoint.width / 2f;
                                Gizmos.DrawLine(current.transform.position + offset + Vector3.up * 0.5f,
                                    NextWayPoint.transform.position + offsetTo + Vector3.up * 0.5f);

                            }
                        }

                    }

                    if (WayPoints[i].branchPoints != null)
                    {
                        foreach (var branchPoint in WayPoints[i].branchPoints)
                        {
                            if (branchPoint != null)
                            {
                                Gizmos.color = Color.red;
                                Gizmos.DrawLine(branchPoint.transform.position + Vector3.up * 0.5f,
                                    WayPoints[i].transform.position + Vector3.up * 0.5f);
                            }
                        }
                    }
                    drawString(WayPoints[i].Index.ToString(), WayPoints[i].transform.position, Color.magenta);
                }
            }
        }
        
        static void drawString(string text, Vector3 worldPos, Color? colour = null) {
            // UnityEditor.Handles.BeginGUI();
            // if (colour.HasValue) GUI.color = colour.Value;
            // var view = UnityEditor.SceneView.currentDrawingSceneView;
            // Vector3 screenPos = view.camera.WorldToScreenPoint(worldPos);
            // Vector2 size = GUI.skin.label.CalcSize(new GUIContent(text)) * 2;
            // GUI.Label(new Rect(screenPos.x - (size.x / 2), -screenPos.y + view.position.height, size.x, size.y), text, EditorStyles.boldLabel);
            // UnityEditor.Handles.EndGUI();
        }
#endif
    }
}
